package com.wushi.smartorm;

import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName OrmMapperHandler
 * @Deacription TODO
 * @Author wushumin
 * @Date 2020/6/3 11:10
 * @Version 1.0
 **/
public class OrmMapperHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Step1解析参数，key=index, value=object
        Map<Integer, Object> paras = parameterHandler(args);
        //解析Step2 sql，key=select/update/insert/delete, value=sql
        Pair<String, String> sqlPair = sqlHandler(proxy, method, paras);
        //Step3,执行sql操作
        OrmExecutor executor = OrmExecutorFactory.getExecutor();
        Map<String, Object> resultMap = executor.proccess(sqlPair);
        //Step4，解析结果
        Object result = resultHandler(resultMap);
        return result;
    }

    /**
     * //注意：getDeclaredField可以返回所有属性，但是getField只能返回public属性！！！
     * @param resultMap
     * @return
     */
    private Object resultHandler(Map<String, Object> resultMap) throws Exception {
        String modelName = MapperConfig.getModel();
        Class<?> modelClass = Class.forName(modelName);
        Object model = modelClass.newInstance();
        for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
            Field field = modelClass.getDeclaredField(getFieldsName(entry.getKey()));
            field.setAccessible(true);
            field.set(model, entry.getValue());
            field.setAccessible(false);
        }
        return model;
    }

    /**
     * 将表列名转换为属性名
     *
     * @param key
     * @return
     */
    private String getFieldsName(String key) {
        String fieldName = null;
        StringBuffer buffer = new StringBuffer();
        if (key.contains("_")) {
            String[] strs = key.split("_");
            for (String str : strs) {
                str = toCapital(str);
            }
            fieldName = String.valueOf(strs);
            return fieldName;
        }else{
            return key;
        }
    }

    /**
     * 首字母大写
     *
     * @param str
     * @return
     */
    private static String toCapital(String str) {
        char c = str.charAt(0);
        if (c >= 'a' && c <= 'z') {
            c += 'A' - 'a';
        }
        return c+str.substring(1);
    }

    /**
     * TODO 后续可以增加TypeHandler进行 java-sql 数据类型映射
     * Map: key-index, value-para Value
     *
     * @param args
     * @return
     */
    private Map<Integer, Object> parameterHandler(Object[] args) {
        Map<Integer, Object> paras = new HashMap<>();
        for (int i = 0; i < args.length; i++) {
            paras.put(i, args[i]);
        }
        return paras;
    }

    /**
     * @param proxy
     * @param method
     * @param paras
     * @return Map: key-key=select/update/insert/delete, value=sql
     */
    private Pair<String, String> sqlHandler(Object proxy, Method method, Map<Integer, Object> paras) {
        String className = proxy.getClass().getName();
        String methodName = method.getName();

        //Step1获取sql map
        Map<String, String> sqlMap = MapperConfig.getSql(className, methodName);
        if (sqlMap == null || sqlMap.size() <= 0) {
            return null;
        }
        Object[] keys = sqlMap.keySet().toArray();
        String sqlType = (String) keys[0];
        Object[] sqls = sqlMap.values().toArray();
        String sql = (String) sqls[0];

        //在sql中注入参数
        Pattern paraPattern = Pattern.compile("\\?");
        Matcher matcher = paraPattern.matcher(sql);
        int i = 0;
        String sqlParsed = null;
        while (matcher.find()) {
            Object para = paras.get(i);
            //对于String类型的参数增加引号
            if (para instanceof String) {
                para = "'" + para + "'";
            }
            //else if( para instanceof Long || para )
            sqlParsed = matcher.replaceFirst(String.valueOf(para));
        }
        Pair<String, String> result = new MutablePair(sqlType, sqlParsed);
        return result;
    }
}
